/*************
<ð.ð displayer ð.ð>
**************/

#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm-i386/uaccess.h>

/*defines*/
#define myMAJOR  256
#define myMINOR  0
#define NUM_OPS 5
/*operaciones auditadas*/
#define LOPEN 0
#define LCLOSE 1
#define LWRITE 2
#define LLSEEK 3
#define LCLONE 4

/*prototipos del modulo watcher*/
void* read_est(int op, int pid_displayer);
void desactiva_watcher(int op);
void activa_watcher(int op);
void reset_est(int pid);
void reset_est_all(void);
/*prototipos de este modulo*/
ssize_t displayer_read (struct file *f, char __user *buff, size_t s, loff_t*off);
int displayer_ioctl (struct inode *i, struct file *f, unsigned int arg1,unsigned long arg2);
int displayer_open (struct inode *i, struct file *f);
int displayer_release (struct inode *i, struct file *f);
int pid;

/*Estructuras*/
struct file_operations oper_displayer = {
    owner: THIS_MODULE,
    read: displayer_read,
    ioctl: displayer_ioctl,
    open: displayer_open,
    release: displayer_release,
};
/*hay que dejar esta estructura para poder hacer un sizeof(t_info) en el read*/
struct t_info{
	int num_entrades;
	int num_sortides_ok;
	int num_sortides_error;
	unsigned long long durada_total;
};


dev_t dev;
struct cdev *my_cdev;
unsigned int abierto = 0;
unsigned int CURRENT_SYSCALL = LOPEN;

/*info displayer*/
MODULE_LICENSE("|×.×| All rights reserved. Copyright® 2010 |×.×|");
MODULE_AUTHOR("David Castaño & Guillermo Muñiz");
MODULE_DESCRIPTION("ProSO driver");

/*
* Inicialització del modul.
*/
static int __init displayer_init(void)
{   /* Codi d’inicialització */
	int err;

	dev = MKDEV(myMAJOR,myMINOR);
	
	if ((err = register_chrdev_region(dev,1,"displayer")) < 0) return err;
	
	my_cdev = cdev_alloc();
	my_cdev->owner = THIS_MODULE;
	my_cdev->ops = &oper_displayer;
	if ((err = cdev_add(my_cdev,dev,1)) < 0) return err;


	printk(KERN_DEBUG "Displayer carregat amb exit\n");
	return 0;
	// Aquesta funció retorna 0 si tot ha
	// anat correctament i < 0 en cas d’error

}

int displayer_open (struct inode *i, struct file *f) {
	/*permiso denegado por no ser root*/
	if (current->uid != 0) return -EPERM;
	
	/*como solo se puede abrir una vez.. */
	if (abierto) return -EBUSY;
	abierto = 1;
	pid = current->pid;

	return 0;
}

ssize_t displayer_read(struct file *f, char __user *buff, size_t s, loff_t *off) {
	int size;	
	unsigned long res;
	res = 0;
	size = s;
	if (s > sizeof(struct t_info)) size = sizeof(struct t_info);
	res = copy_to_user(buff,read_est(CURRENT_SYSCALL,pid),size);
	return (ssize_t)(size - res);
}

int displayer_ioctl(struct inode *i, struct file *f, unsigned int arg1, unsigned long arg2) {
	int pid2,res;
	switch(arg1) {
		case 0: 
			if((void*)arg2 == NULL) {
				pid2 = current->pid;
			}
			else if ((res = copy_from_user(&pid2,(void*)arg2,sizeof(int))) < 0) return res;
			if (find_task_by_pid(pid2) == NULL) return -ESRCH;
	printk(KERN_DEBUG "he cambiado el pid: %d ----> %d\n\n",pid,pid2);
			pid = pid2;
			return 0;
			break;
		case 1: 
			CURRENT_SYSCALL = arg2;
			break;
		case 2:
			reset_est(pid);
			break;
		case 3:
			reset_est_all();
			break;
		case 4:
			activa_watcher(arg2);
			break;
		case 5: 
			desactiva_watcher(arg2);
			break;
		default:
			return -EINVAL;
	}

	return 0;
}

int displayer_release (struct inode *i, struct file *f) {
	/*soltamos el dispositivo */
	abierto = 0;
	return 0;
}


/*
* Descarrega el módul.
*/
static void __exit displayer_exit(void)
{
/* Codi de finalització */
	cdev_del(my_cdev);
	unregister_chrdev_region(dev,1);
	printk(KERN_DEBUG "Displayer descarregat amb exit\n");
}
/*No compila por estar estas dos lineas repetidas en los dos ficheros*/
module_init(displayer_init);
module_exit(displayer_exit);
